package demo.security.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自定义realm，需要继承AuthorizingRealm父类
 *   重写父类中的两个方法
 *   doGetAuthorizationInfo ：授权
 *   doGetAuthenticationInfo ：认证
 */
public class CustomRealm extends AuthorizingRealm {

  /**
   * 用userMap拟数据库
   */
  Map<String, String> userMap = new HashMap<>();

  {
    // 设置加密算法
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("md5");
    matcher.setHashIterations(1);
    this.setCredentialsMatcher(matcher);

    // 模拟数据库用户表数据
    String plainPwd = "123456";
    // 明文-->密文，加盐
    Md5Hash md5Hash = new Md5Hash(plainPwd, "Mark");
    // 数据库里不会存密码明文，而是存密文
    userMap.put("Mark", md5Hash.toString());

  }

  @Override
  public void setName(String name) {
    super.setName("customRealm");
  }

  /**
   * 授权
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

    String userName = (String) principalCollection.getPrimaryPrincipal();

    Set<String> roles = getRolesByUserName(userName);
    Set<String> permissions = getPermissionsByUserName(userName);

    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    simpleAuthorizationInfo.setRoles(roles);
    simpleAuthorizationInfo.setStringPermissions(permissions);
    return simpleAuthorizationInfo;
  }

  /**
   * 模拟从数据库获得用户的权限数据
   */
  private Set<String> getPermissionsByUserName(String userName) {
    Set<String> permissions = new HashSet<>();
    permissions.add("user:delete");
    permissions.add("user:add");
    return permissions;
  }

  /**
   * 模拟从数据库获得用户的角色数据
   */
  private Set<String> getRolesByUserName(String userName) {
    Set<String> roles = new HashSet<>();
    roles.add("admin");
    roles.add("user");
    return roles;
  }

  /**
   * 认证
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 1.从subject传过来的认证信息中，获得用户名
    String userName = (String) authenticationToken.getPrincipal();

    // 2.通过用户名到数据库中获取凭证
    String password = getPasswordByUserName(userName);
    if (password == null) {
      return null;
    }

    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, getName());
    // 如果密码有加盐，需要多设置一下setCredentialsSalt
    authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("Mark"));

    return authenticationInfo;
  }

  /**
   * 从数据库表中，根据用户名查询其密码
   */
  private String getPasswordByUserName(String userName) {
    // 这里用map来模拟
    return userMap.get(userName);
  }
}
